If I relaunch the server immediately after quitting, the initialization calls
complete without error, but the server never receives any incoming connection
requests. If I wait several minutes before restarting the server, this problem
doesn't occur. It appears that there is some internal timeout for disconnected
connections. Is there a solution to this problem so that the server can be
launched without waiting for the timeout?
A TCP has a 2-minute timeout on a binding after a
connection has closed before
the same port can be bound to again. This prevents stale data from corrupting a
new connection. For this reason, you see a delay before you can successfully
bind to the port again.
There is a way around this, using the IP_REUSEADDR option and the OTOptionManagement call. Set this option on all of your listening endpoints before you bind, and the problem should disappear.
Important - even after using the IP_REUSEADDR option, at most one endpoint that is in a state less than connected (listening; unbound doesn't count) may be bound to a given port. Any number of connected or closing endpoints may be so bound to other unique ports, however.
The following sample will help to show how to set this option. The function takes 2 input parameters, the EndpointRef that you want to set the option for, and the state of the option that you want, typically, true. The function returns a result of OSStatus. If the result is less than zero, then it it the result of making the OTOptionManagement call. If the result is positive, then the call completed successfully, but the status field had a value other than T_SUCCESS.
#include <OpenTransport.h> // open transport files #include <OpenTptInternet.h> /* input: noDelayState - true - nodelay // false - normal delay state // // output: if result less that kOTNoError, then the result of // OTOptionManagement call is returned // otherwise, the status value is returned as defined in // OpenTransport.h T_SUCCESS = 0x020, return kOTNoError if SUCCESS T_FAILURE = 0x040, T_PARTSUCCESS = 0x100, T_READONLY = 0x200, T_NOTSUPPORT = 0x400 */ OSStatus DoNegotiateIPReuseAddrOption(EndpointRef ep, Boolean reuseState) { UInt8 buf[kOTFourByteOptionSize]; // define buffer for fourByte // Option size TOption* opt; // option ptr to make items // easier to access TOptMgmt req; OSStatus err; Boolean isAsync = false; opt = (TOption*)buf; // set option ptr to buffer req.opt.buf = buf; req.opt.len = sizeof(buf); req.opt.maxlen = sizeof(buf); // were using ret for the // return result also. req.flags = T_NEGOTIATE; // negotiate for option opt->level = INET_IP; // dealing with an IP Level // function opt->name = IP_REUSEADDR; opt->len = kOTFourByteOptionSize; *(UInt32*)opt->value = reuseState; // set the desired option // level, true or false if (OTIsSynchronous(ep) == false) // check whether ep sync or // not { isAsync = true; // set flag if async OTSetSynchronous(ep); // set endpoint to sync } err = OTOptionManagement(ep, &req, &req); if (isAsync == true) // restore ep state if // necessary OTSetAsynchronous(ep); // if no error then check the option status value if (err == kOTNoError) { if (opt->status != T_SUCCESS) // if not T_SUCCESS, return // the status err = opt->status; } // otherwise return kOTNOError return err; }